﻿/*
   Copyright 2011 Softisis

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

using System;
using System.Linq.Expressions;
using FOM.Interfaces;
using FOM.Utils;

namespace FOM.Impl
{
    /// <summary>
    /// Standard implementation of a mapping.
    /// </summary>
    /// <remarks>Copyright Softisis 2011 (France)</remarks>
    /// <typeparam name="TSrc">Type of the source object</typeparam>
    /// <typeparam name="TDst">Type of the destination object</typeparam>
    public class MappingNodeImpl<TSrc, TDst> : AbstractMappingNode, IMappingNode<TSrc, TDst>
    {
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="srcpath">Path to the source property</param>
        /// <param name="dstpath">Path to the target property</param>
        public MappingNodeImpl(String srcpath, String dstpath)
        {
            SourceType = typeof (TSrc);
            TargetType = typeof (TDst);

            NodePathLink = new PathLink(srcpath, dstpath);
        }

        public virtual IMappingNode<TPropertyS, TPropertyD> Map<TPropertyS, TPropertyD>(
            Expression<Func<TSrc, TPropertyS>> expsrc,
            Expression<Func<TDst, TPropertyD>> expdst)
        {
            var newmap=PathHelper.BuildMappingFromExpression(expsrc, expdst);
            AddMapping(newmap);
            return newmap;
        }

        public virtual IMappingNode<TProperty, TProperty> Map<TProperty>(Expression<Func<TSrc, TProperty>> expr)
        {
            return Map<TProperty, TProperty>(expr, null);
        }

        public virtual IMappingNode<TSrc, TDst> Exclude<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc, Expression<Func<TDst, TPropertyD>> expdst)
        {
            var newmap = PathHelper.BuildMappingFromExpression(expsrc, expdst);
            AddExclusionMapping(newmap);
            return this;
        }

        public virtual IMappingNode<TSrc, TDst> Exclude<TPropertyS>(Expression<Func<TSrc, TPropertyS>> expsrc)
        {
            return Exclude<TPropertyS, TPropertyS>(expsrc, null);
        }

        public virtual IMappingNode<TSrc, TDst> NewInstance()
        {
            return NewInstance(false);
        }

        public IMappingNode<TSrc, TDst> NewInstance(bool reuseCached)
        {
            CreateNewInstance = true;
            ReuseCachedInstance = reuseCached;
            return this;
        }

        public virtual IMappingNode<TPropertyS, TPropertyD> GoTo<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc, Expression<Func<TDst, TPropertyD>> expdst)
        {
            var newmap = PathHelper.BuildGotoLinkFromExpression(expsrc, expdst);
            AddMapping(newmap);
            return newmap;
        }

        public virtual IMappingNode<TSrc, TReplacement> OverrideTargetType<TReplacement>()
        {
            // we create a proxy for fluent continuity
            ProxyOverTypeNodeImpl<TSrc, TReplacement> repl=new ProxyOverTypeNodeImpl<TSrc, TReplacement>(this);
            TargetOverrideType = typeof (TReplacement);
            return repl;
        }

        public virtual IMappingNode<TReplacement, TDst> OverrideSourceType<TReplacement>()
        {
            // we create a proxy for fluent continuity
            ProxyOverTypeNodeImpl<TReplacement, TDst> repl = new ProxyOverTypeNodeImpl<TReplacement, TDst>(this);
            SourceOverrideType = typeof(TReplacement);
            return repl;
        }

        public virtual IMappingNode<TSrc, TDst> UseConverter(IConverter converter)
        {
            Converter = converter;
            return this;
        }

        public virtual IMappingNode<TSrc, TDst> UseCondition(ICondition cond)
        {
            Condition = cond;
            return this;
        }

        public IMappingNode<TDst, TSrc> Reverse()
        {
            return (MappingNodeImpl<TDst, TSrc>) ReverseNode();
        }

        public virtual bool Remove<TPropertyS, TPropertyD>(Expression<Func<TSrc, TPropertyS>> expsrc, Expression<Func<TDst, TPropertyD>> expdst)
        {
            var newmap = PathHelper.BuildGotoLinkFromExpression(expsrc, expdst);
            return RemoveMapping(newmap);
        }

        internal virtual MappingNodeImpl<TDst, TSrc> GetInstance()
        {
            return new MappingNodeImpl<TDst, TSrc>(NodePathLink.TargetPath, NodePathLink.SourcePath);
        }

        public override AbstractMappingNode ReverseNode()
        {
            var newone = GetInstance();
            newone.TargetOverrideType = SourceOverrideType;
            newone.SourceOverrideType = TargetOverrideType;
            newone.NodePathLink = NodePathLink == null ? null : NodePathLink.Reverse();
            
            if (Excluded!=null)
                foreach (var pl in Excluded)
                    newone.Excluded.Add(pl.Reverse());

            newone.CreateNewInstance = CreateNewInstance;
            newone.ConvertBeforeTest = ConvertBeforeTest;
            newone.UseCondition(Condition == null ? null : Condition.Reverse());
            newone.UseConverter(Converter == null ? null : Converter.Reverse());
            newone.ReuseCachedInstance = ReuseCachedInstance;

            if (Children != null)
                foreach (var c in Children)
                    newone.AddMapping(c.ReverseNode());
            return newone;
        }
    }
}
